package com.atguigu.tingshu.common.login;


import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.security.auth.login.LoginException;

@Aspect
@Component
public class LoginAspect {

    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 环绕通知方法要具备四个特点：
     *  1.方法必须返回Object对象
     *  2.方法必须要有ProceedingJoinPoint参数
     *  3.必须手动执行目标方法
     *  4.建议抛出Throwable异常
     */
    @Around("@annotation(login)")
    public Object around(ProceedingJoinPoint joinPoint,Login login) throws Throwable {
        //1.从请求头信息中获取token
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //为什么要转换类型?
        HttpServletRequest request = requestAttributes.getRequest();
        String token = request.getHeader("token");

        //2.判断token是否为kon
        UserInfoVo userInfoVo = null;
        if (StringUtils.isNotBlank(token)){
            //如果token不为空,则根据token查询redis中的登录状态
             userInfoVo = (UserInfoVo) this.redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
        }
        //3.判断首付必须登录并且为登录
        if (login.required() && userInfoVo ==null){
            throw  new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        //4.把登录信息放入ThreadLocal中
        if (userInfoVo!=null){
            AuthContextHolder.setUserId(userInfoVo.getId());
            AuthContextHolder.setUsername(userInfoVo.getNickname());
        }

        //执行目标方法
        Object result = joinPoint.proceed(joinPoint.getArgs());

        //6.为了防止内存泄漏,释放threadLocal中的内容
        AuthContextHolder.removeUserId();
        AuthContextHolder.removeUsername();

        return result;
    }
}
